[CFX-5789] [CFX-5790] ToolRegistry & Surface environment-aware failure messaging#577
Conversation
|
|
||
| fmt.Fprint(w, msg) | ||
|
|
||
| return installed, fmt.Errorf("install failed for %q (exit code %d)", prerequisite.Name, exitCode) |
There was a problem hiding this comment.
Start flow hides install failures
High Severity
Detailed install failure text is written only to the io.Writer, while the returned error is a short exit-code message. The quickstart TUI installs deps into a buffer and on failure displays only m.err, so users lose tips, the tried command, and streamed command output that this change added.
Triggered by project rule: Bugbot Rules for DataRobot CLI
Reviewed by Cursor Bugbot for commit 29b422a. Configure here.
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6c10ba7. Configure here.
| // Unix-like systems (Linux & macOS) return 126 when a file lacks execute permissions | ||
| if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && exitCode == 126 { | ||
| return true | ||
| } |
There was a problem hiding this comment.
Exit 126 ignored on Windows
Medium Severity
Permission-denied handling treats exit code 126 only when runtime.GOOS is linux or darwin. Installs still run via sh -c on Windows (e.g. Git Bash), where 126 can mean “not executable,” but without matching stderr text the failure is not classified as permission denied and users miss the Administrator-oriented tip.
Additional Locations (1)
Triggered by project rule: Bugbot Rules for DataRobot CLI
Reviewed by Cursor Bugbot for commit 6c10ba7. Configure here.
| func (fs FallbackStrategy) getStrategyTip(goos string) string { | ||
| cmds := fs.Commands | ||
|
|
||
| if goos == "windows" && len(fs.CommandsWindows) > 0 { |
There was a problem hiding this comment.
nit here:
Do we have reusable values in this repo. so we're not using magic strings for things like "windows" and likely "darwin" and "linux" ?
There was a problem hiding this comment.
"windows", "darwin", and "linux" are all potential values of runtime.GOOS, which is a build-time constant.
https://pkg.go.dev/internal/goos#GOOS
https://cs.opensource.google/go/go/+/refs/tags/go1.26.4:src/internal/goos/zgoos_linux.go;l=7
I am not recommending this because I'm not convinced it's worth it here but one way to approach this would be to conditionally compile the fallbackstrategy code using go build. (You can extend packages based on runtime.GOOS and runtime.GOARCH.) A working example of this can be found in internal/workload where Wojciech has implemented OS-specific diskspace implementations.
- https://github.com/datarobot-oss/cli/blob/main/internal/workload/sync/diskspace_unix.go
- https://github.com/datarobot-oss/cli/blob/main/internal/workload/sync/diskspace_windows.go
What MIGHT be worth doing is something like this, where we enumerate the OS we actually support.
type OS string
const (
OSDarwin OS = "darwin"
OSLinux OS = "linux"
OSWindows OS = "windows"
)
func IsWindows(os OS) bool { return os == OSWindows }
func IsUnixLike(os OS) bool { return os == OSDarwin || os == OSLinux }
There was a problem hiding this comment.
This is all just informational though, since I know @taras-pokornyy you want a bigger discussion on how we can scale this registry.
There was a problem hiding this comment.
I think it is a good idea to have constants for GOOS instead of magic strings but not in the scope of this PR 🤷♂️
| return "" | ||
|
|
||
| case 1: | ||
| return " Try: " + cmds[0] |
There was a problem hiding this comment.
So wasn't sure where to put this.
Curious what you and @ajalon1 and others think.
I like your usage of \t elsewhere and wonder if that would be better to use than the many places you're using double spaces??
| return " Try: " + cmds[0] | |
| return "\tTry: " + cmds[0] |
To me it's:
- less likely to accidentally have one or three spaces at some point as a UI bug
- easier to read in the code as opposed to empty spaces - maybe that's just me though
There was a problem hiding this comment.
I think it'd be good to stick to a single convention, but that becomes a renderer / UI issue and I think we can do this better with a more structured formatting approach than just \t. text/tabwriter is a helpful package if we want to go that route.
There was a problem hiding this comment.
That's a good point.
First level indention was two spaces. Next level indention was "\t" (It is much longer).
Created a const TAB which is two spaces and used that for indentions.
First level indention is just TAB
Next level indention is TAB+TAB
Commit : dc1017f
c-h-russell-walker
left a comment
There was a problem hiding this comment.
Looks great - made some nit comments. And thanks for the demo today during our meeting.
ajalon1
left a comment
There was a problem hiding this comment.
This LGTM as is. The registry could use some more work, but that is out of scope.
The one thing I would suggest addressing is what happens with strategy.WithVersion(""). You can either do that here or in a followup.
| func (fs FallbackStrategy) getStrategyTip(goos string) string { | ||
| cmds := fs.Commands | ||
|
|
||
| if goos == "windows" && len(fs.CommandsWindows) > 0 { |
There was a problem hiding this comment.
"windows", "darwin", and "linux" are all potential values of runtime.GOOS, which is a build-time constant.
https://pkg.go.dev/internal/goos#GOOS
https://cs.opensource.google/go/go/+/refs/tags/go1.26.4:src/internal/goos/zgoos_linux.go;l=7
I am not recommending this because I'm not convinced it's worth it here but one way to approach this would be to conditionally compile the fallbackstrategy code using go build. (You can extend packages based on runtime.GOOS and runtime.GOARCH.) A working example of this can be found in internal/workload where Wojciech has implemented OS-specific diskspace implementations.
- https://github.com/datarobot-oss/cli/blob/main/internal/workload/sync/diskspace_unix.go
- https://github.com/datarobot-oss/cli/blob/main/internal/workload/sync/diskspace_windows.go
What MIGHT be worth doing is something like this, where we enumerate the OS we actually support.
type OS string
const (
OSDarwin OS = "darwin"
OSLinux OS = "linux"
OSWindows OS = "windows"
)
func IsWindows(os OS) bool { return os == OSWindows }
func IsUnixLike(os OS) bool { return os == OSDarwin || os == OSLinux }
| func (fs FallbackStrategy) getStrategyTip(goos string) string { | ||
| cmds := fs.Commands | ||
|
|
||
| if goos == "windows" && len(fs.CommandsWindows) > 0 { |
There was a problem hiding this comment.
This is all just informational though, since I know @taras-pokornyy you want a bigger discussion on how we can scale this registry.
| return "" | ||
|
|
||
| case 1: | ||
| return " Try: " + cmds[0] |
There was a problem hiding this comment.
I think it'd be good to stick to a single convention, but that becomes a renderer / UI issue and I think we can do this better with a more structured formatting approach than just \t. text/tabwriter is a helpful package if we want to go that route.
|
Hey @c-h-russell-walker , @ajalon1 ! Going to merge this guy tonight. |
ajalon1
left a comment
There was a problem hiding this comment.
Still LGTM pending smoke test results
|
/approve-smoke-tests |
|
🔐 Fork PR smoke tests triggered by @taras-pokornyy What happens next:
|
|
🔐 Fork smoke tests started by maintainer ⏳ Security scans passed. Running smoke tests... Commit: |
|
✅ All smoke tests passed! (Fork PR) ✅ Security Scan: success |


RATIONALE
When
dr deps installruns a tool's install command and it fails, the previous error was a bare exit-code line with no guidance on what to try next. This change wires up the existingToolRegistrydata to produce actionable, environment-aware tips when an install command fails — "You have pyenv — try: pip install uv" — and falls back to the canonical install command (e.g. curl | sh) when no manager is detected. Permission-denied failures (exit code 126 or matching stderr text) get an OS-specific "re-run with sudo / as Administrator" message instead.CHANGES
PR Automation
Comment-Commands: Trigger CI by commenting on the PR:
/trigger-smoke-testor/trigger-test-smoke- Run smoke tests/trigger-install-testor/trigger-test-install- Run installation testsLabels: Apply labels to trigger workflows:
run-smoke-testsorgo- Run smoke tests on demand (only works for non-forked PRs)Important
For Forked PRs: The
run-smoke-testslabel won't work. A required Smoke Tests check will block merge until a maintainer acts:/approve-smoke-teststo run smoke tests (results will set the check)/skip-smoke-teststo bypass the check without running testsPlease comment requesting a maintainer review if you need smoke tests to run.
Note
Low Risk
UX and messaging only around failed installs; success path unchanged. Suggestions are advisory shell commands, not auto-executed.
Overview
When
dr deps installfails, users now get structured output on the writer (not only a minimal error): what was tried, optional tips, retry command, and docs URL.InstallPrerequisitestees install output into a buffer so failures can be classified and messaged.A new
ToolRegistrymaps tools (python, uv, node, pulumi, task, git) to ordered install strategies (pyenv/brew/asdf/winget/etc.) plus fallbacks.DetectEnvironmentprobes PATH and NVM layout;selectInstallStrategypicks the first matching manager, skipping the manager that just failed.buildInstallFailureMsg/buildInstallTipadd OS-specific guidance for permission errors (exit 126 / stderr heuristics) vs alternate install commands (e.g. “You have pyenv — try: pip install uv”). Returned errors are shortened; detail lives in stdout.Broad unit coverage was added for registry, strategy selection, and failure messaging.
Reviewed by Cursor Bugbot for commit 6c10ba7. Configure here.